package lb

import (
	"strings"
	"sync"
	"sync/atomic"
	"time"
)

type Apps struct {
	apps map[string]*App
}

func NewApps() *Apps {
	a := &Apps{
		apps: make(map[string]*App),
	}
	return a
}

func (a *Apps) Get(name string) *App {
	if app, ok := a.apps[name]; ok {
		return app
	}
	return nil
}

func (a *Apps) Add(app App) {
	if eapp, ok := a.apps[app.Name]; ok {
		//copy
		for _, ins := range app.Instances {
			eapp.Add(ins)
		}

	} else {
		if app.lb == nil {
			app.lb = &RoundRobin{}
		}
		a.apps[app.Name] = &app
	}
}

type App struct {
	Name      string
	Instances []*HostInstance
	lb        Balancer
}

func (a *App) Get(key string) *HostInstance {
	ins := a.lb.Next(key, a.Instances)
	return ins
}

func (a *App) Add(ins *HostInstance) {
	for _, node := range a.Instances {
		//如果存在就修改状态
		if node.InstanceId == ins.InstanceId {
			node.Status = ins.Status
		} else {
			//不存在就添加
			a.Instances = append(a.Instances, ins)
		}
	}
}
func (a *App) Remove(ins *HostInstance) {
	for i, node := range a.Instances {
		//如果存在就删除
		if node.InstanceId == ins.InstanceId {
			a.Instances = append(a.Instances[:i], a.Instances[i:]...)
		}
	}
}

//全量更新，增加新的，移除不存在的，更新存在的
func (a *App) UpdateInstances(sis []*HostInstance) {
	//更新和添加
	for _, node := range sis {
		//如果存在就修改状态
		a.Add(node)
	}

	//删除不存在的实例
	for i, node := range a.Instances {
		hasExists := false
		for _, newNode := range sis {
			if node.InstanceId == newNode.InstanceId {
				hasExists = true
				break
			}
		}
		//不存在就删除
		if !hasExists {
			a.Instances = append(a.Instances[:i], a.Instances[i:]...)
		}

	}
}

//服务实例状态类型
type Status string

const (
	StatusEnabled  Status = "enabled"
	StatusDisabled Status = "disabled"
)

//服务实例
type HostInstance struct {
	InstanceId string //实例ID，应用名称:主机名:端口
	Address    string //服务实例地址,主机名:端口
	Status     Status //服务实例状态
	//
	LastUpdatedTimestamp int64
	OverriddenStatus     string
	Version              string //实例应用版本号
	//
	Weight        int32 //配置的权重值
	CurrentWeight int32 // 当前权重
	EffectWeight  int32 //受影响权重

	//
	LastSleepOpenTime          *time.Time //最后Sleep打开时间
	LastSleepExpectedCloseTime *time.Time //最后Sleep预计关闭时间
	LastSleepWindowSeqCount    int        //最后连续Sleep统计
	IsFailedSleepOpen          bool
	IsNotFound                 bool

	lock *sync.Mutex
}

func (h *HostInstance) SetCurrentWeight(newCurrentWeight int32) {
	//atomic.StoreInt32(&h.CurrentWeight, newCurrentWeight)
	atomic.SwapInt32(&h.CurrentWeight, newCurrentWeight)
}

func (h *HostInstance) ResetFailedSleepFlag() {
	h.lock.Lock()
	defer h.lock.Unlock()
	h.LastSleepOpenTime = nil
	h.LastSleepExpectedCloseTime = nil
	h.LastSleepWindowSeqCount = 0
	h.IsFailedSleepOpen = false
}

func (h *HostInstance) SetFailedSleepFlagForSeqMode(sleepDuration time.Duration) {
	h.lock.Lock()
	defer h.lock.Unlock()
	lastSleepOpenTime := time.Now()
	//如果多次连续Sleep的第一次，则设置第一次Sleep时间
	if h.LastSleepOpenTime == nil {
		h.LastSleepOpenTime = &lastSleepOpenTime
	} else {
		lastSleepOpenTime = *h.LastSleepOpenTime
	}
	closeTime := lastSleepOpenTime.Add(sleepDuration)
	h.LastSleepExpectedCloseTime = &closeTime
	h.LastSleepWindowSeqCount++
	h.IsFailedSleepOpen = true
}

func (h *HostInstance) SetFailedSleepFlagForFixedMode(sleepDuration time.Duration) {
	h.lock.Lock()
	defer h.lock.Unlock()
	lastSleepOpenTime := time.Now()
	h.LastSleepOpenTime = &lastSleepOpenTime
	closeTime := lastSleepOpenTime.Add(sleepDuration)
	h.LastSleepExpectedCloseTime = &closeTime

	h.LastSleepWindowSeqCount++
	h.IsFailedSleepOpen = true
}

//复制原生属性
func (h *HostInstance) CopyFrom(source *HostInstance) {
	h.InstanceId = source.InstanceId
	//h.isInit = source.isInit
	if h.EffectWeight == h.Weight {
		h.EffectWeight = source.Weight
	}
	h.Weight = source.Weight
	h.Address = source.Address
	h.Status = source.Status
	h.LastUpdatedTimestamp = source.LastUpdatedTimestamp
	h.OverriddenStatus = source.OverriddenStatus
}
func (h *HostInstance) Ip() string {
	return strings.Split(h.Address, ":")[0]
}
func (h *HostInstance) Port() string {
	return strings.Split(h.Address, ":")[1]
}

//片排序
type ByCurrentWeight []*HostInstance

func (p ByCurrentWeight) Len() int {
	return len(p)
}

func (p ByCurrentWeight) Less(i, j int) bool {
	return p[i].CurrentWeight >= p[j].CurrentWeight
}

func (p ByCurrentWeight) Swap(i, j int) {
	p[i], p[j] = p[j], p[i]
}
